//
// Created by LiuYou on 2021/11/25.
//

#ifndef HTTP_SERVER_THREADPOOL_HPP
#define HTTP_SERVER_THREADPOOL_HPP


#include <pthread.h>
#include <list>
#include <vector>

#include "../Sync/MutexLock.hpp"
#include "../Sync/Sem.hpp"


/**
 * @brief 线程池, 创建8个线程, 来争抢丢进来的任务。
 * @tparam TaskObj  模板参数TaskObj为任务类。main线程扔到线程池中的任务对象。
 *
 * @details 使用了一个请求队列解除了main线程和工作线程的耦合关系: main线程往请求队列中插入任务,
 * 线程池中的工作线程通过竞争取得请求队列中的任务并处理。
 *
 * @note 没有考虑异常。
 * @since 0.1
 * @author ly
 *
 * @version 0.1.1
 */
template<typename TaskObj>
class ThreadPool final {
public:
    /**
     * @brief 创建一个线程池。
     * @param threadNum
     * @param requestQueueMax
     *
     * @details 如何让工作线程可以访问到请求队列(即访问到ThreadPool类中的请求队列)呢?
     * 有两种方法:
     * <p>1. 通过类的静态对象来调用, 例如单例模式。在工作线程中调用单例对象的方法。</p>
     * <p>2. 将类的对象作为参数传递给工作线程, 然后在工作线程中调用类的成员或者方法。</p>
     *
     * @note 没有考虑异常。
     * @since 0.1
     */
    explicit ThreadPool(int threadNum = 8, int requestQueueMax = 10000) :
            threadNum( threadNum ), requestQueueMax( requestQueueMax ), isStop( false ) {

        if ((threadNum <= 0) || (requestQueueMax <= 0)) {
            fprintf( stderr, "创建线程池失败: "
                             "if ((threadNum <= 0) || (requestQueueMax <= 0)) { "
                             ", 请指定合法的线程池工作线程个数, 或者请求队列的最大长度。\n" );
//            throw std::logic_error( "传入参数非法。--> 创建线程池失败: "
//                                    "if ((threadNum <= 0) || (requestQueueMax <= 0)) { "
//                                    ", 请指定合法的线程池工作线程个数, 或者请求队列的最大长度。" );
        }

//        threads = new pthread_t[threadNum];
//        threadsArray.resize( threadNum );
        threadsArray.reserve( threadNum );
//        if (threads == nullptr) {
//            fprintf( stderr, "创建线程池失败: "
//                             "threads = new pthread_t[threadNum]; "
//                             ", 申请内存失败。\n" );
//        }

        for (int i = 0; i < this->threadNum; ++i) {
            int errNum = pthread_create(
                    /*threads + i*/threadsArray.data() + i, nullptr,
                                   [](void* arg) -> void* {
                                       auto threadPool = static_cast<ThreadPool<TaskObj>*>(arg);
                                       threadPool->run();
                                       pthread_exit( threadPool );
                                   },
                                   this );
            if (errNum != 0) {
//                delete[] threads;
                fprintf( stderr, "创建线程池失败: pthread_create(): %s.\n", strerror( errNum ));
//                throw std::runtime_error( std::string( "创建线程池失败: pthread_create(): " ) + strerror( errNum ) + "." );
            }

//            errNum = pthread_detach( threads[i] );
            errNum = pthread_detach( threadsArray[i] );
            if (errNum != 0) {
//                delete[] threads;
                fprintf( stderr, "创建线程池失败: pthread_detach(): %s.\n", strerror( errNum ));
//                throw std::runtime_error( std::string( "创建线程池失败: pthread_detach(): " ) + strerror(errno) + "." );
            }

            fprintf( stdout, "创建了线程池中的第 %d 线程。\n", i + 1 );

        }

        fprintf( stdout, "当前的线程池中有 %d 个线程。", threadNum );

    }

    ~ThreadPool() {
//        delete[] threads;
        isStop = true;
    }

    ThreadPool(const ThreadPool& rhs) = delete;

    ThreadPool& operator=(const ThreadPool& rhs) = delete;


    /**
     * @brief 供main线程调用, 往线程池中丢任务。
     * @param taskObj 任务对象的地址。
     * @return 是否插入请求队列成功。
     */
    [[deprecated("函数已弃用, 会在下一个版本中删除。")]]
    bool appendTask(TaskObj* taskObj) {
        mutexLock.lock();
        if (requestQueue.size() > requestQueueMax) {
            mutexLock.unlock();
            return false;
        }
        requestQueue.push_back( taskObj );
        mutexLock.unlock();
        taskObjSem.post();
        return true;
    }

    /**
     * @brief 供main线程调用, 往线程池中丢任务。
     * @param taskObj 任务对象的地址。
     * @return 是否插入请求队列成功。
     *
     * @since 0.1.1
     */
    bool appendTaskObj(TaskObj* taskObj) {
        {
            MutexLockGuard lock( mutexLock );
            if (requestQueue.size() > requestQueueMax) {
                return false;
            }
            requestQueue.push_back( taskObj );
        }

        taskObjSem.post();
        return true;
    }

private:
    /**
     * @brief 线程池中的工作线程调用。
     *
     * @since 0.1.1
     */
    void run() {

        while (!isStop) {
            taskObjSem.wait();

            TaskObj* taskObj = nullptr;
            {
                MutexLockGuard lock( mutexLock );
                if (requestQueue.empty()) {
                    continue;
                }
                taskObj = requestQueue.front();
                requestQueue.pop_front();
            }

            if (taskObj == nullptr) {
                continue;
            }

            taskObj->process();

        }

    }

private:
    /** 线程池中的线程数量, 默认为8个线程。可以指定。*/
    int threadNum;

    /** 请求队列允许的最大请求数, 默认10,000个, 即1万个。可以指定。*/
    int requestQueueMax;

    /** 使用一个动态分配的数组, 来存储线程池中各个线程的tid。*/
    [[deprecated("字段已弃用, 在下一个版本中删除。")]]
    pthread_t* threads;

    /**
     * 使用 vector容器 来存储线程池中各个线程的 tid。
     *
     * @since 0.1.1
     */
    std::vector<pthread_t> threadsArray;

    /** 请求队列。*/
    std::list<TaskObj*> requestQueue;

    /** 此字段用来控制线程池销毁时所有线程的正常退出。*/
    bool isStop;

    /**
     * @brief ThreadPool class 的内部字段加锁保护, 使 ThreadPool类 为线程安全的类。
     * @details
     * 因为public接口方法: bool appendTask(TaskObj* taskObj);
     * 内部对 requestQueue 字段进行了访问。而 requestQueue 这个对象会在多线程下访问到,
     * 所以要加锁保护 requestQueue , 所以使用了互斥量来保护内部字段。
     *
     */
    MutexLock mutexLock;

    /**
     * 对字段 requestQueue 加入信号量机制, 设计为一个类似 生产者-消费者 的模式。
     */
    Sem taskObjSem;


};


#endif //HTTP_SERVER_THREADPOOL_HPP
